use crate::byte_data_reader::ByteDataReader;

#[derive(Clone)]
pub enum CFConstantInfoEnum {
    CFConstantInfoInvalid(CFConstantInfoInvalid),
    CFConstantInfoClass(CFConstantInfoClass),
    CFConstantInfoString(CFConstantInfoString),
    CFConstantInfoInteger(CFConstantInfoInteger),
    CFConstantInfoFieldRef(CFConstantInfoFieldRef),
    CFConstantInfoMethodRef(CFConstantInfoMethodRef),
    CFConstantInfoInterfaceMethodRef(CFConstantInfoInterfaceMethodRef),
    CFConstantInfoFloat(CFConstantInfoFloat),
    CFConstantInfoLong(CFConstantInfoLong),
    CFConstantInfoDouble(CFConstantInfoDouble),
    CFConstantInfoNameAndType(CFConstantInfoNameAndType),
    CFConstantInfoUTF8(CFConstantInfoUTF8),
    CFConstantInfoMethodHandle(CFConstantInfoMethodHandle),
    CFConstantInfoMethodType(CFConstantInfoMethodType),
    CFConstantInfoInvokeDynamicInfo(CFConstantInfoInvokeDynamicInfo),
}

/// 无效常量
#[derive(Clone)]
pub struct CFConstantInfoInvalid {}

pub const CFCONSTANT_INFO_INVALID: CFConstantInfoInvalid = CFConstantInfoInvalid {};

/*
CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoClass {
    pub cp_index: u16,
}

/*
CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoString {
    pub string_index: u16,
}

/*
CONSTANT_Fieldref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoFieldRef {
    pub cp_class_index: u16,
    pub name_and_type_index: u16,
}

#[derive(Clone)]
pub struct CFConstantInfoMethodRef {
    cp_class_index: u16,
    name_and_type_index: u16,
}


#[derive(Clone)]
pub struct CFConstantInfoInterfaceMethodRef {
    cp_class_index: u16,
    name_and_type_index: u16,
}

/*
CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoInteger {
    val: i32,
}

/*
CONSTANT_Float_info {
    u1 tag;
    u4 bytes;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoFloat {
    val: f32,
}

/*
CONSTANT_Long_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoLong {
    pub val: i64,
}

/*
CONSTANT_Double_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoDouble {
    val: f64,
}

/*
CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoNameAndType {
    pub name_index: u16,
    pub descriptor_index: u16,
}

/*
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}
*/
#[derive(Clone)]
pub struct CFConstantInfoUTF8 {
    pub length: u16,
    pub string: String,
}

/*
CONSTANT_MethodHandle_info {
    u1 tag;
    u1 reference_kind;
    u2 reference_index;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoMethodHandle {
    reference_kind: u8,
    reference_index: u16,
}

/*
CONSTANT_MethodType_info {
    u1 tag;
    u2 descriptor_index;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoMethodType {
    descriptor_index: u16,
}

/*
CONSTANT_InvokeDynamic_info {
    u1 tag;
    u2 bootstrap_method_attr_index;
    u2 name_and_type_index;
}
*/
#[derive(Clone)]
pub struct CFConstantInfoInvokeDynamicInfo {
    bootstrap_method_attr_index: u16,
    name_and_type_index: u16,
}

const CONSTANT_INFO_TAG_CLASS: u8 = 7;
const CONSTANT_INFO_TAG_FIELD_REF: u8 = 9;
const CONSTANT_INFO_TAG_METHOD_REF: u8 = 10;
const CONSTANT_INFO_TAG_INTERFACE_METHOD_REF: u8 = 11;
const CONSTANT_INFO_TAG_STRING: u8 = 8;
const CONSTANT_INFO_TAG_INTEGER: u8 = 3;
const CONSTANT_INFO_TAG_FLOAT: u8 = 4;
const CONSTANT_INFO_TAG_LONG: u8 = 5;
const CONSTANT_INFO_TAG_DOUBLE: u8 = 6;
const CONSTANT_INFO_TAG_NAME_AND_TYPE: u8 = 12;
const CONSTANT_INFO_TAG_UTF8: u8 = 1;
const CONSTANT_INFO_TAG_METHOD_HANDLE: u8 = 15;
const CONSTANT_INFO_TAG_METHOD_TYPE: u8 = 16;
const CONSTANT_INFO_TAG_INVOKE_DYNAMIC: u8 = 18;

pub fn create_constant_info(reader: &mut ByteDataReader)
                            -> Result<CFConstantInfoEnum, String> {
    let tag = reader.read_u8();
    return match tag {
        CONSTANT_INFO_TAG_CLASS => {
            Ok(CFConstantInfoEnum::CFConstantInfoClass(CFConstantInfoClass {
                cp_index: reader.read_u16(),
            }))
        }

        CONSTANT_INFO_TAG_FIELD_REF => {
            Ok(CFConstantInfoEnum::CFConstantInfoFieldRef(
                CFConstantInfoFieldRef {
                    cp_class_index: reader.read_u16(),
                    name_and_type_index: reader.read_u16(),
                }
            ))
        }
        CONSTANT_INFO_TAG_METHOD_REF => {
            Ok(CFConstantInfoEnum::CFConstantInfoMethodRef(CFConstantInfoMethodRef {
                cp_class_index: reader.read_u16(),
                name_and_type_index: reader.read_u16(),
            }))
        }
        CONSTANT_INFO_TAG_INTERFACE_METHOD_REF => {
            Ok(CFConstantInfoEnum::CFConstantInfoInterfaceMethodRef(CFConstantInfoInterfaceMethodRef {
                cp_class_index: reader.read_u16(),
                name_and_type_index: reader.read_u16(),
            }))
        }
        CONSTANT_INFO_TAG_STRING => {
            Ok(CFConstantInfoEnum::CFConstantInfoString(CFConstantInfoString {
                string_index: reader.read_u16()
            }))
        }
        CONSTANT_INFO_TAG_INTEGER => {
            Ok(CFConstantInfoEnum::CFConstantInfoInteger(CFConstantInfoInteger {
                val: reader.read_i32()
            }))
        }
        CONSTANT_INFO_TAG_FLOAT => {
            Ok(CFConstantInfoEnum::CFConstantInfoFloat(CFConstantInfoFloat {
                val: reader.read_f32()
            }))
        }
        CONSTANT_INFO_TAG_LONG => {
            Ok(CFConstantInfoEnum::CFConstantInfoLong(CFConstantInfoLong {
                val: reader.read_i64()
            }))
        }
        CONSTANT_INFO_TAG_DOUBLE => {
            Ok(CFConstantInfoEnum::CFConstantInfoDouble(CFConstantInfoDouble {
                val: reader.read_f64()
            }))
        }
        CONSTANT_INFO_TAG_NAME_AND_TYPE => {
            Ok(CFConstantInfoEnum::CFConstantInfoNameAndType(CFConstantInfoNameAndType {
                name_index: reader.read_u16(),
                descriptor_index: reader.read_u16(),
            }))
        }
        CONSTANT_INFO_TAG_UTF8 => {
            let len = reader.read_u16();
            Ok(CFConstantInfoEnum::CFConstantInfoUTF8(CFConstantInfoUTF8 {
                length: len,
                string: reader.read_mutf8_str(len),
            }))
        }
        CONSTANT_INFO_TAG_METHOD_HANDLE => {
            Ok(CFConstantInfoEnum::CFConstantInfoMethodHandle(CFConstantInfoMethodHandle {
                reference_kind: reader.read_u8(),
                reference_index: reader.read_u16(),
            }))
        }
        CONSTANT_INFO_TAG_METHOD_TYPE => {
            Ok(CFConstantInfoEnum::CFConstantInfoMethodType(CFConstantInfoMethodType {
                descriptor_index: reader.read_u16()
            }))
        }
        CONSTANT_INFO_TAG_INVOKE_DYNAMIC => {
            Ok(CFConstantInfoEnum::CFConstantInfoInvokeDynamicInfo(CFConstantInfoInvokeDynamicInfo {
                bootstrap_method_attr_index: reader.read_u16(),
                name_and_type_index: reader.read_u16(),
            }))
        }
        _ => {
            Err(format!("Error Tag:{}", tag))
        }
    };
}